`include "csr.vh"
module mmu
#(
parameter TLBNUM = 16
)
(
    input  wire         clk,
    input  wire         resetn,
    input  wire [31:0]  inst_addr,
    input  wire [31:0]  data_addr,
    output wire [31:0]  inst_sram_addr,
    output wire [31:0]  data_sram_addr,
// from csr
    input  wire [31:0]  csr_crmd,
    input  wire [31:0]  csr_dmw0,
    input  wire [31:0]  csr_dmw1,
    input  wire [31:0]  csr_asid,
    input  wire [31:0]  csr_tlbehi,
    input  wire [31:0]  csr_tlbidx,
    input  wire [31:0]  csr_tlbelo0,
    input  wire [31:0]  csr_tlbelo1,

    input  wire tlbsrch_valid,
    input  wire tlbrd_valid,
    input  wire tlbwr_valid,
    input  wire tlbfill_valid,
    input  wire invtlb_valid,

    output wire tlbsrch_found,
    output wire [ 3:0]  tlbsrch_index,

    output wire [88:0] tlbrd_pageinfo,

    input  wire [ 4:0] invtlb_op,
    input  wire [ 9:0] invtlb_asid,
    input  wire [31:0] invtlb_va,

    input  wire data_load,
    input  wire data_store,
    output wire ex_adef,
    output wire ex_adem,
    output wire ex_pif,
    output wire ex_ipe,
    output wire ex_pil,
    output wire ex_pis,
    output wire ex_pme,
    output wire ex_ppi,
    output wire fs_ex_tlbr,
    output wire es_ex_tlbr
);
wire crmd_da, crmd_pg, dmw0_plv0, dmw0_plv3, dmw1_plv0, dmw1_plv3;
wire [ 1:0] crmd_plv, dmw0_mat, dmw1_mat;
wire [ 2:0] dmw0_vseg, dmw0_pseg, dmw1_vseg, dmw1_pseg;
wire [ 9:0] asid_asid;
wire [ 7:0] asid_asidbits;
wire [18:0] tlbehi_vppn;
wire [ 3:0] tlbidx_index;
wire [ 5:0] tlbidx_ps;
wire        tlbidx_ne;
wire        tlbelo0_v, tlbelo1_v, tlbelo0_d, tlbelo1_d, tlbelo0_g, tlbelo1_g;
wire [ 1:0] tlbelo0_plv, tlbelo1_plv, tlbelo0_mat, tlbelo1_mat;
wire [19:0] tlbelo0_ppn, tlbelo1_ppn;
assign crmd_plv     = csr_crmd[`CSR_CRMD_PLV];
assign crmd_da      = csr_crmd[`CSR_CRMD_DA];
assign crmd_pg      = csr_crmd[`CSR_CRMD_PG];
assign dmw0_plv0    = csr_dmw0[`CSR_DMW_PLV0];
assign dmw0_plv3    = csr_dmw0[`CSR_DMW_PLV3];
assign dmw0_mat     = csr_dmw0[`CSR_DMW_MAT];
assign dmw0_vseg    = csr_dmw0[`CSR_DMW_VSEG];
assign dmw0_pseg    = csr_dmw0[`CSR_DMW_PSEG];
assign dmw1_plv0    = csr_dmw1[`CSR_DMW_PLV0];
assign dmw1_plv3    = csr_dmw1[`CSR_DMW_PLV3];
assign dmw1_mat     = csr_dmw1[`CSR_DMW_MAT];
assign dmw1_vseg    = csr_dmw1[`CSR_DMW_VSEG];
assign dmw1_pseg    = csr_dmw1[`CSR_DMW_PSEG];
assign tlbehi_vppn  = csr_tlbehi[`CSR_TLBEHI_VPPN];
assign tlbidx_index = csr_tlbidx[`CSR_TLBIDX_INDEX];
assign tlbidx_ps    = csr_tlbidx[`CSR_TLBIDX_PS];
assign tlbidx_ne    = csr_tlbidx[`CSR_TLBIDX_NE];
assign asid_asid    = csr_asid[`CSR_ASID_ASID];
assign asid_asidbits= csr_asid[`CSR_ASID_ASIDBITS];
assign tlbelo0_v    = csr_tlbelo0[`CSR_TLBELO_V];
assign tlbelo0_d    = csr_tlbelo0[`CSR_TLBELO_D];
assign tlbelo0_plv  = csr_tlbelo0[`CSR_TLBELO_PLV];
assign tlbelo0_mat  = csr_tlbelo0[`CSR_TLBELO_MAT];
assign tlbelo0_g    = csr_tlbelo0[`CSR_TLBELO_G];
assign tlbelo0_ppn  = csr_tlbelo0[`CSR_TLBELO_PPN];
assign tlbelo1_v    = csr_tlbelo1[`CSR_TLBELO_V];
assign tlbelo1_d    = csr_tlbelo1[`CSR_TLBELO_D];
assign tlbelo1_plv  = csr_tlbelo1[`CSR_TLBELO_PLV];
assign tlbelo1_mat  = csr_tlbelo1[`CSR_TLBELO_MAT];
assign tlbelo1_g    = csr_tlbelo1[`CSR_TLBELO_G];
assign tlbelo1_ppn  = csr_tlbelo1[`CSR_TLBELO_PPN];

assign fs_ex_tlbr   = crmd_pg && !s0_found && !inst_dmw_hit;
assign ex_adef      = inst_addr[1:0] != 2'h0 || (crmd_pg && inst_addr[31] && !inst_dmw_hit);
assign ex_pif       = crmd_pg && s0_found && !s0_v && !inst_dmw_hit;
assign ex_ipe       = ((crmd_pg && s0_found && s0_v) || inst_addr[31]) && (crmd_plv == 2'h3 && s0_plv == 2'h0) && !inst_dmw_hit;
assign es_ex_tlbr   = crmd_pg && !s1_found && (data_load || data_store) && !data_dmw_hit;
assign ex_adem      = crmd_pg && (data_load || data_store) && data_addr[31] && !data_dmw_hit;
assign ex_pil       = crmd_pg && s1_found && !s1_v && data_load && !data_dmw_hit;
assign ex_pis       = crmd_pg && s1_found && !s1_v && data_store && !data_dmw_hit;
assign ex_ppi       = crmd_pg && s1_found && s1_v && (crmd_plv == 2'h3 && s1_plv == 2'h0) && (data_load || data_store) && !data_dmw_hit;
assign ex_pme       = crmd_pg && s1_found && s1_v && (crmd_plv == 2'h0 || s1_plv == 2'h3) && !s1_d && data_store && !data_dmw_hit;

wire [31:0] inst_dmw_addr, data_dmw_addr, inst_tlb_addr, data_tlb_addr;
wire [31:0] inst_page_addr, data_page_addr;
wire inst_dmw_hit, data_dmw_hit;
wire inst_dmw0_hit, inst_dmw1_hit, data_dmw0_hit, data_dmw1_hit;

assign inst_page_addr = inst_dmw_hit ? inst_dmw_addr : inst_tlb_addr;
assign data_page_addr = data_dmw_hit ? data_dmw_addr : data_tlb_addr;

assign inst_sram_addr = {32{crmd_da}} & inst_addr | {32{crmd_pg}} & inst_page_addr & 32'hfffffffc;
assign data_sram_addr = {32{crmd_da}} & data_addr | {32{crmd_pg}} & data_page_addr & 32'hfffffffc;

// 直接映射
assign inst_dmw_addr =  inst_dmw0_hit ? {dmw0_pseg, inst_addr[28:0]} :
                        inst_dmw1_hit ? {dmw1_pseg, inst_addr[28:0]} :
                        inst_addr;
assign data_dmw_addr =  data_dmw0_hit ? {dmw0_pseg, data_addr[28:0]} :
                        data_dmw1_hit ? {dmw1_pseg, data_addr[28:0]} :
                        data_addr;

    assign inst_dmw_hit  = inst_dmw0_hit || inst_dmw1_hit;
    assign data_dmw_hit  = data_dmw0_hit || data_dmw1_hit;
    assign inst_dmw0_hit = inst_addr[31:29] == dmw0_vseg && 
                            (crmd_plv == 2'h0 && dmw0_plv0 || crmd_plv == 2'h3 && dmw0_plv3);
    assign inst_dmw1_hit = inst_addr[31:29] == dmw1_vseg && 
                            (crmd_plv == 2'h0 && dmw1_plv0 || crmd_plv == 2'h3 && dmw1_plv3);
    assign data_dmw0_hit = data_addr[31:29] == dmw0_vseg && 
                            (crmd_plv == 2'h0 && dmw0_plv0 || crmd_plv == 2'h3 && dmw0_plv3);
    assign data_dmw1_hit = data_addr[31:29] == dmw1_vseg && 
                            (crmd_plv == 2'h0 && dmw0_plv0 || crmd_plv == 2'h3 && dmw1_plv3);
// 页表映射
assign inst_tlb_addr = {32{s0_found}} & {s0_ppn, inst_addr[11:0]};
assign data_tlb_addr = {32{s1_found}} & {s1_ppn, data_addr[11:0]};

//search port
wire [19:0] s0_ppn, s1_ppn;
wire [18:0] s0_vppn, s1_vppn;
wire [ 9:0] s0_asid, s1_asid;
wire [ 5:0] s0_ps, s1_ps;
wire [$clog2(TLBNUM)-1:0] s0_index, s1_index;
wire [ 1:0] s0_mat, s0_plv, s1_mat, s1_plv;
wire        s0_va_bit12, s0_found, s0_d, s0_v, s1_va_bit12, s1_found, s1_d, s1_v;
//write port
wire [19:0] w_ppn0, w_ppn1;
wire [18:0] w_vppn;
wire [ 9:0] w_asid;
wire [ 5:0] w_ps;
wire [$clog2(TLBNUM)-1:0] w_index;
wire [ 1:0] w_plv0, w_mat0, w_plv1, w_mat1;
wire        tlb_we, w_e, w_g, w_d0, w_v0, w_d1, w_v1;
//read port
wire [19:0] r_ppn0, r_ppn1;
wire [18:0] r_vppn;
wire [ 9:0] r_asid;
wire [ 5:0] r_ps;
wire [$clog2(TLBNUM)-1:0] r_index;
wire [ 1:0] r_plv0, r_mat0, r_plv1, r_mat1;
wire        r_e, r_g, r_d0, r_v0, r_d1, r_v1;

// search port
assign s0_vppn      = inst_addr[31:13];
assign s0_va_bit12  = inst_addr[12];
assign s0_asid      = asid_asid;
assign s1_vppn      =   invtlb_valid ?    invtlb_va[31:13]: 
                        tlbsrch_valid ?   tlbehi_vppn:
                                    data_addr[31:13];
assign s1_va_bit12  = data_addr[12];
assign s1_asid      = invtlb_valid? invtlb_asid : asid_asid;
assign tlbsrch_found= s1_found;
assign tlbsrch_index= s1_index;

wire [18:0] tlbsrch_vppn;
wire [ 9:0] tlbsrch_asid;

assign r_index  = tlbidx_index;
assign tlbrd_pageinfo = {
    r_ppn0, r_ppn1, r_vppn, r_asid, r_ps, r_plv0, r_mat0, r_plv1, r_mat1, r_e, r_g, r_d0, r_v0, r_d1, r_v1
};


// write port
assign tlb_we   = tlbwr_valid || tlbfill_valid;
assign w_index  = tlbfill_valid ? inst_addr[5:2] : tlbidx_index;    //随机数，可以随便截取别的信号
assign w_e      = ~tlbidx_ne;
assign w_vppn   = tlbehi_vppn;
assign w_asid   = invtlb_valid ? invtlb_asid : asid_asid;
assign w_g      = tlbelo0_g & tlbelo1_g;    //当且仅当elo0和elo1的g位都�?1
assign w_ps     = tlbidx_ps;
assign w_ppn0   = tlbelo0_ppn;
assign w_plv0   = tlbelo0_plv;
assign w_mat0   = tlbelo0_mat;
assign w_d0     = tlbelo0_d;
assign w_v0     = tlbelo0_v;
assign w_ppn1   = tlbelo1_ppn;
assign w_plv1   = tlbelo1_plv;
assign w_mat1   = tlbelo1_mat;
assign w_d1     = tlbelo1_d;
assign w_v1     = tlbelo1_v;

tlb tlb(
    .clk(clk),
    .resetn(resetn),
    .s0_vppn(s0_vppn),
    .s0_asid(s0_asid),
    .s0_va_bit12(s0_va_bit12),
    .s0_found(s0_found),
    .s0_index(s0_index),
    .s0_ppn(s0_ppn),
    .s0_ps(s0_ps),
    .s0_mat(s0_mat),
    .s0_d(s0_d),
    .s0_v(s0_v),
    .s0_plv(s0_plv),
    .s1_vppn(s1_vppn),
    .s1_asid(s1_asid),
    .s1_va_bit12(s1_va_bit12),
    .s1_found(s1_found),
    .s1_index(s1_index),
    .s1_ppn(s1_ppn),
    .s1_ps(s1_ps),
    .s1_mat(s1_mat),
    .s1_d(s1_d),
    .s1_v(s1_v),
    .s1_plv(s1_plv),
    .invtlb_valid(invtlb_valid),
    .invtlb_op(invtlb_op),
    .we(tlb_we),
    .w_index(w_index),
    .w_e(w_e),
    .w_vppn(w_vppn),
    .w_asid(w_asid),
    .w_g(w_g),
    .w_ps(w_ps),
    .w_ppn0(w_ppn0),
    .w_plv0(w_plv0),
    .w_mat0(w_mat0),
    .w_d0(w_d0),
    .w_v0(w_v0),
    .w_ppn1(w_ppn1),
    .w_plv1(w_plv1),
    .w_mat1(w_mat1),
    .w_d1(w_d1),
    .w_v1(w_v1),
    .r_index(r_index),
    .r_e(r_e),
    .r_vppn(r_vppn),
    .r_ps(r_ps),
    .r_asid(r_asid),
    .r_g(r_g),
    .r_ppn0(r_ppn0),
    .r_plv0(r_plv0),
    .r_mat0(r_mat0),
    .r_d0(r_d0),
    .r_v0(r_v0),
    .r_ppn1(r_ppn1),
    .r_plv1(r_plv1),
    .r_mat1(r_mat1),
    .r_d1(r_d1),
    .r_v1(r_v1)
);

endmodule